package com.zxb.core.log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.zxb.core.common.C;
import com.zxb.core.common.R;
import com.zxb.core.common.Req;
import com.zxb.core.common.User;
import com.zxb.core.common.exception.BusinessException;
import com.zxb.core.util.JsonUtil;
import com.zxb.core.util.TokenUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Map;

/**
 * @author Zxb
 */
@Aspect
@Component
public class LogRequestAspect {

    private final ThreadPoolTaskExecutor taskExecutor;

    public LogRequestAspect(ThreadPoolTaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    @Pointcut("@annotation(com.zxb.core.log.LogRequest)")
    public void logRequestPointCut() {
    }

    @Around("logRequestPointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws JsonProcessingException, NoSuchMethodException {
        long startTime = System.currentTimeMillis();

        SysLog sysLog = new SysLog();

        Class<?> clazz = pjp.getTarget().getClass();
        String className = clazz.getSimpleName();
        Signature signature = pjp.getSignature();
        String methodName = signature.getName();
        String token = Req.getHeader(C.TOKEN_KEY);
        User u = TokenUtil.validateToken(token);
        String username = u == null ? null : u.getUsername();
        long requestTime = System.currentTimeMillis();
        StringBuilder requestParamsBuilder = new StringBuilder();
        Map<String, String[]> reqMap = Req.getParameterMap();
        if (reqMap != null) {
            for (String key : reqMap.keySet()) {
                String[] values = reqMap.get(key);
                requestParamsBuilder.append(key);
                requestParamsBuilder.append(":");
                requestParamsBuilder.append((values != null && values.length > 0) ? values[0] : JsonUtil.OM.writeValueAsString(values));
                requestParamsBuilder.append("&");
            }
        }
        String requestParam = requestParamsBuilder.length() > 0 ? (requestParamsBuilder.substring(0, requestParamsBuilder.length() - 1)).replaceAll("'", "\\\\'") : null;
        Method method = clazz.getMethod(methodName, ((MethodSignature) signature).getParameterTypes());
        LogRequest ann = method.getAnnotation(LogRequest.class);

        if (ann != null) {
            sysLog.setRequestCode(ann.value());
        }
        sysLog.setRequestIp(Req.getIp());
        sysLog.setClassName(className);
        sysLog.setMethodName(methodName);
        sysLog.setToken(token);
        sysLog.setUsername(username);
        sysLog.setRequestTime(new Timestamp(requestTime));
        sysLog.setRequestParam(requestParam);
        sysLog.setRequestUri(Req.getURI());
        Object result;
        try {
            result = pjp.proceed();
            sysLog.setState(2);
        } catch (Throwable e) {
            if (e instanceof BusinessException) {
                BusinessException exp = (BusinessException) e;
                result = R.error(exp.getCode(), exp.getMsg());
            } else {
                result = R.error();
            }
            e.printStackTrace();
            sysLog.setState(3);
            sysLog.setExpInfo(ExceptionUtil.getRootCauseMessage(e));
        }

        //执行后更新日志
        long spend = System.currentTimeMillis() - startTime;
        String responseJson = JsonUtil.OM.writeValueAsString(result).replaceAll("'", "\\\\'");

        sysLog.setRequestResult(responseJson);
        sysLog.setSpend(spend);
        taskExecutor.execute(() -> {
            //save log
        });
        return result;
    }

}
